Identity Federationを使うIdentity BrokerをJavaで書いてみた
IAMのID統合(Identity Federation)機能
IAMが新しく提供を開始したID統合は、企業内やアプリケーション内で使っている既存のアカウントと認証を用いてAWSの各種リソースやAPIにアクセスできる一時的なセキュリティ証明書を発行する機能です。ビジネスアプリのアカウント毎に新たにAWS/IAMアカウントを発行する必要が無いため使いやすいですね。
Identity Brokerとは
Identity Brokerはその名の通り仲介役をする機能です。既存のアプリがどのような認証をするのか各自異なりますので、このブローカーは自作する必要があります。AWSのWEBサイトでは.NET C#を使ってActiveDirectoryとID統合する方法が書いてあります。今回はできるだけ実装をシンプルにするためにモックを用いてJavaによるID統合を実現しています。
ID統合を表現するデモアプリケーションの図
Javaで作ってみました
ID統合を表現するデモアプリケーションの図に沿って、Javaで一連の動作をするアプリを作ってみました。以下がクラスの関係図です。緑色の部分はAWSが提供しているサービスです。青いのはAWS SDKで提供されているクラスです。紫がエントリポイントで、赤がブローカー、オレンジがLDAP認証のフリをするモックです。
IdentityBroker.java
IdentityBrokerクラスは、getTokenメソッドを提供しています。これは、BusinessApplicationクラスから呼び出されるもので、始めにLDAPServiceMockへの認証を行った後に、問題なければ一時セキュリティ証明書を発行するという流れです。戻り値として、Credentialsインスタンスが返されています。証明書には有効期限が設定されています。このサンプルでは3600秒です。また、ポリシーを設定を設定することもできます。このサンプルでは、policy.txtにS3へのアクセス許可を書きました。このように、一時発行される証明書に対して権限や有効期限を設定することができます。
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.PropertiesCredentials; import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClient; import com.amazonaws.services.securitytoken.model.Credentials; import com.amazonaws.services.securitytoken.model.GetFederationTokenRequest; import com.amazonaws.services.securitytoken.model.GetFederationTokenResult; public class IdentityBroker { private static Integer duration = 3600; public static Credentials getToken(String name,String pass) throws IOException{ //check account LDAPServiceMock ldap = new LDAPServiceMock(); Account account = ldap.login(name, pass); if(account == null)return null; //property InputStream in = IdFedSample.class.getResourceAsStream("AwsCredentials.properties"); AWSCredentials awsCredentials = new PropertiesCredentials(in); AWSSecurityTokenServiceClient stsClient = new AWSSecurityTokenServiceClient(awsCredentials); InputStream is = IdFedSample.class.getResourceAsStream("policy.txt"); String policy = getText(is); //request GetFederationTokenRequest request = new GetFederationTokenRequest(); request.withDurationSeconds(duration).withPolicy(policy).withName(name); //result GetFederationTokenResult result = stsClient.getFederationToken(request); Credentials fCredentials = result.getCredentials(); return fCredentials; } private static String getText(InputStream is) throws IOException{ String policy=""; InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); for (;;) { String text = br.readLine(); if (text == null) break; policy += text; } return policy; } }
LDAPServiceMock.java
user1という名前ならばAccountインスタンスを返します。違うならばnullを返します。
public class LDAPServiceMock { public Account login(String name,String pass){ if("user1".equals(name)){ //this is mock method Account account = new Account(); account.id = "00001"; account.name = name; account.email = "[email protected]"; return account; }else{ return null; } } }
BusinessApplication.java
アプリ全体のエントリーポイントとなるクラスです。起動時に引数を渡します。名前とパスです。IdentityBrokerのgetTokenメソッドによって一時セキュリティ証明書を取得します。その証明書を使って、Amazon S3とAmazon EC2に接続を試みています。一時セキュリティ証明書では、S3へのアクセスポリシーだけ付与されていますので、S3への操作は成功し、EC2への操作に失敗することが分かります。
import java.io.IOException; import java.util.List; import com.amazonaws.auth.BasicSessionCredentials; import com.amazonaws.services.ec2.AmazonEC2Client; import com.amazonaws.services.ec2.model.DescribeRegionsResult; import com.amazonaws.services.ec2.model.Region; import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.model.ObjectListing; import com.amazonaws.services.s3.model.S3ObjectSummary; import com.amazonaws.services.securitytoken.model.Credentials; public class BusinessApplication { public static void main(String args[]) throws IOException{ if(args.length < 2){ System.out.println("BusinessApplication name pass"); System.exit(0); } Credentials credentials = IdentityBroker.getToken(args[0], args[1]); if(credentials == null){ System.out.println("Account Failure"); System.exit(0); } BasicSessionCredentials basicSessionCredentials = new BasicSessionCredentials( credentials.getAccessKeyId(), credentials.getSecretAccessKey(), credentials.getSessionToken()); AmazonS3Client s3 = new AmazonS3Client(basicSessionCredentials); String bucketName = "www.akari7.net"; ObjectListing objects = s3.listObjects(bucketName); List<S3ObjectSummary> list = objects.getObjectSummaries(); for (S3ObjectSummary s3ObjectSummary : list) { System.out.println("Object Name: " + s3ObjectSummary.getKey()); } AmazonEC2Client ec2 = new AmazonEC2Client(basicSessionCredentials); DescribeRegionsResult r2 = ec2.describeRegions(); List<Region> l2 = r2.getRegions(); for (Region region : l2) { System.out.println("Region Name: " + region.getRegionName()); } } }
ポリシーファイル
今回のサンプルで一時セキュリティ証明書に付与されたポリシーは以下です。
{ "Statement":[{ "Effect":"Allow", "Action":"s3:ListBucket", "Resource":"arn:aws:s3:::www.akari7.net" }] }
実行する。
例えば以下のようにコマンドから実行します。
BusinessApplication user1 pass
結果は以下です。S3へのアクセスに成功し、Bucket内のObject一覧が表示されました。一方で、EC2へのアクセスでは、UnauthorizedOperationということで許可されて無く、操作に失敗してしまいました。
2011/09/20 19:29:43 com.amazonaws.http.AmazonHttpClient executeHelper 情報: Sending Request: POST https://sts.amazonaws.com / Parameters: (Name: user1, Action: GetFederationToken, SignatureMethod: HmacSHA256, DurationSeconds: 3600, Policy: { "Statement":[{ "Effect":"Allow", "Action":"s3:ListBucket", "Resource":"arn:aws:s3:::www.akari7.net" }]}, AWSAccessKeyId: arxgasgxdsxdszfxd, SignatureVersion: 2, Version: 2011-06-15, Signature: xzdsxfzdfzdzgfzsxzfvx+yYLYRFGuvlPU=, Timestamp: 2011-09-20T10:29:43.258Z, ) 2011/09/20 19:29:46 com.amazonaws.http.AmazonHttpClient handleResponse 情報: Received successful response: 200, AWS Request ID: 75f94721-e373-11e0-bbb0-9336b8d8ae4e 2011/09/20 19:29:46 com.amazonaws.http.AmazonHttpClient executeHelper 情報: Sending Request: GET https://www.akari7.net.s3.amazonaws.com / Headers: (Content-Type: application/x-www-form-urlencoded; charset=utf-8, ) 2011/09/20 19:29:47 com.amazonaws.http.AmazonHttpClient handleResponse 情報: Received successful response: 200, AWS Request ID: aaskjnxfaglefgxasq324 Object Name: hello.txt 2011/09/20 19:29:47 com.amazonaws.http.AmazonHttpClient executeHelper 情報: Sending Request: POST https://ec2.amazonaws.com / Parameters: (Action: DescribeRegions, SignatureMethod: HmacSHA256, SecurityToken: AQoDYXdzEIz//////////wEa8AGYc9acgnlaflaskmxfhdfVi/QWWjFVeoosdYZDs9MofSUyWh2/44DrA5IJdixoEjfKvqHvZ610G15D6QfcKbq4f3yFTAWme67ecX4QX5WlhWW9oiUhEHSI9O1pdPgsgmtbh8wQ=, AWSAccessKeyId: ASIAJCT6RJXSJWLT6D5A, SignatureVersion: 2, Version: 2011-05-15, Signature: sdxddfsdfsdfxzdfxzfzfgdzxdx/gPLc=, Timestamp: 2011-09-20T10:29:47.092Z, ) 2011/09/20 19:29:49 com.amazonaws.http.AmazonHttpClient handleErrorResponse 情報: Received error response: Status Code: 403, AWS Request ID: dqwerf-d6f9-44f3-bb02-809ee2de74a2, AWS Error Code: UnauthorizedOperation, AWS Error Message: You are not authorized to perform this operation. Exception in thread "main" Status Code: 403, AWS Request ID: d7d723456f-d6f9-44f3-bb02-809ee2de74a2, AWS Error Code: UnauthorizedOperation, AWS Error Message: You are not authorized to perform this operation. at com.amazonaws.http.AmazonHttpClient.handleErrorResponse(AmazonHttpClient.java:500) at com.amazonaws.http.AmazonHttpClient.executeHelper(AmazonHttpClient.java:262) at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:166) at com.amazonaws.services.ec2.AmazonEC2Client.invoke(AmazonEC2Client.java:4996) at com.amazonaws.services.ec2.AmazonEC2Client.describeRegions(AmazonEC2Client.java:686) at com.amazonaws.services.ec2.AmazonEC2Client.describeRegions(AmazonEC2Client.java:3999) at BusinessApplication.main(BusinessApplication.java:39)
まとめ
AWS Identity and Access ManagementのIdentity Federationは、一時セキュリティ証明書を発行することで、AWSやIAMのアカウントを持っていないユーザーにAWSのサービスを利用できるようにする機能であることが分かりました。また、Identity Brokerを用意することで、自社の環境に合わせた認証と組み合わせて活用できることも分かりました。ID統合の仕組みを理解して楽しく証明書を発行しましょう!